/*
 * Copyright (C) 2025 Tsingteng MicroSystem
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file rw_watch_dog.c
 * @brief 读卡WatchDog功能实现
 */

#include "nci_feature.h"
#if IS_FEATURE_ON(NCI_FEATURE_RW_EXT)

#include "nfa_rw.h"
#include "rw_int.h"
#include "nci_task.h"
#include "discover_int.h"

static tTHREAD s_watch_dog_thread;
static tNCI_RW_WDST s_state = NCI_RW_WDST_STOPED;
static uint32_t s_delay_time = 0;

static void *watch_dog_thread(void *args) {
    NFCLOG_I("starting background rw watch dog thread");
    tNCI_STATUS ret = nfa_rw_request_lock();
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("rw lock failed, ret = %d", ret);
        return NULL;
    }
    if (s_state != NCI_RW_WDST_REQUEST_START) {
        NFCLOG_E("curr state is not NCI_RW_WDST_REQUEST_START, state=%d", s_state);
    } else {
        s_state = NCI_RW_WDST_RUNNING;
    }
    uint32_t presence_check_delay = s_delay_time;
    NFCLOG_I("presence_check_delay = %dms", presence_check_delay);
    while (s_state == NCI_RW_WDST_RUNNING || s_state == NCI_RW_WDST_PAUSED) {
        tNFC_STATUS wait_ret = osi_mutex_wait(rw_get_mutex(), presence_check_delay);
        if (wait_ret != NFC_STATUS_OK && wait_ret != NFC_STATUS_TIMEOUT) {
            NFCLOG_E("wait failed, ret = %d", wait_ret);
            continue;
        }
        if (s_state != NCI_RW_WDST_RUNNING && s_state != NCI_RW_WDST_PAUSED) {
            NFCLOG_I("s_state is not NCI_RW_WDST_RUNNING or NCI_RW_WDST_PAUSED, state=%d", s_state);
            break;
        }
        if (s_state == NCI_RW_WDST_RUNNING) {
            ret = rw_presence_check_no_lock();
            if (ret != NCI_STATUS_OK) {
                NFCLOG_E("presence check failed, ret = %d", ret);
                ret = rf_deactivate(false); // 切换到Discovery状态
                // 发送WATCH_DOG_STOP，让task线程调用join方法回收线程资源
                nci_task_post(NCI_TASK_EVT_RW_WATCH_DOG_STOP, 0, 0, NULL);
                goto unlock;
            }
        }
    }
unlock:
    // 设置状态为STATE_STOPED_WAIT_JOIN，等待调用JOIN回收线程资源
    s_state = NCI_RW_WDST_STOPED_WAIT_JOIN;
    nfa_rw_release_lock();
    NFCLOG_I("background rw watch dog thread exit");
    return NULL;
}

void rw_watch_dog_set_state_no_lock(bool is_paused) {
    if (s_state != NCI_RW_WDST_RUNNING && s_state != NCI_RW_WDST_PAUSED) {
        NFCLOG_E("rw watch dog thread is not running, state=%d", s_state);
        return;
    }
    NFCLOG_I("set rw watch dog is_paused = %d", is_paused);
    s_state = is_paused ? NCI_RW_WDST_PAUSED : NCI_RW_WDST_RUNNING;
    osi_mutex_notify_all(rw_get_mutex());
}

tNCI_STATUS nfa_rw_watch_dog_set_state(bool is_paused) {
    tNCI_STATUS ret = nfa_rw_request_lock();
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("rw lock failed, ret = %d", ret);
        return ret;
    }
    rw_watch_dog_set_state_no_lock(is_paused);
    nfa_rw_release_lock();
    return NCI_STATUS_OK;
}

tNCI_STATUS nfa_rw_watch_dog_start(uint32_t presence_check_delay) {
    tNCI_STATUS ret = nfa_rw_request_lock();
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("rw lock failed, ret = %d", ret);
        return ret;
    }

    if (!nfa_rw_get_activated_tag()) {
        NFCLOG_E("no tag activated");
        ret = NCI_STATUS_EXT_TAG_LOST;
        goto unlock;
    }

    if (s_state != NCI_RW_WDST_STOPED) {
        NFCLOG_E("rw watch dog thread is running, state=%d", s_state);
        ret = NCI_STATUS_EXT_FAILED;
        goto unlock;
    }
    s_delay_time = presence_check_delay;
    s_state = NCI_RW_WDST_REQUEST_START;
    tTHREAD_PARAMS thread_params = {
        .entry = watch_dog_thread,
        .name = "presence_check",
        .priority = OS_DEFAULT_THREAD_PRIO,
        .stack_size = 0,
        .arg = NULL,
        .joinable = true,
    };
    tNFC_STATUS thread_create_ret = osi_thread_create(&s_watch_dog_thread, &thread_params);
    if (thread_create_ret != NFC_STATUS_OK) {
        NFCLOG_E("create rw watch dog thread failed, ret = %d", thread_create_ret);
        ret = NCI_STATUS_EXT_FAILED;
        goto unlock;
    }
    ret = NCI_STATUS_OK;

unlock:
    nfa_rw_release_lock();
    return ret;
}

tNCI_STATUS nfa_rw_watch_dog_stop() {
    tNCI_STATUS ret = nfa_rw_request_lock();
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("rw lock failed, ret = %d", ret);
        return ret;
    }
    switch (s_state) {
        case NCI_RW_WDST_STOPED:
            NFCLOG_W("rw watch dog thread is not running, state=%d", s_state);
            ret = NCI_STATUS_OK;
            break;
        case NCI_RW_WDST_REQUEST_START:
        case NCI_RW_WDST_RUNNING:
        case NCI_RW_WDST_PAUSED:
            // 设置状态为STATE_REQUEST_STOP，通知线程退出
            s_state = NCI_RW_WDST_REQUEST_STOP;
            osi_mutex_notify_all(rw_get_mutex());
            __attribute__((fallthrough)); // 往下继续执行
        case NCI_RW_WDST_REQUEST_STOP:
        case NCI_RW_WDST_STOPED_WAIT_JOIN:
            nfa_rw_release_lock(); // 释放锁，让watch dog线程能够获取到锁并退出
            // 等待线程退出并释放资源
            if (osi_thread_join(s_watch_dog_thread, NULL) != NFC_STATUS_OK) {
                NFCLOG_E("join rw watch dog thread failed");
                return NCI_STATUS_EXT_FAILED; // 此处未持有锁可以直接return
            }
            // 再次获取锁，设置状态为STATE_STOPED
            ret = nfa_rw_request_lock();
            if (ret != NCI_STATUS_OK) {
                NFCLOG_E("rw lock failed, ret = %d", ret);
                return ret; // 此处获取锁失败可以直接return
            }
            s_state = NCI_RW_WDST_STOPED;
            ret = NCI_STATUS_OK;
            break;
        default:
            NFCLOG_E("unknown state=%d", s_state);
            ret = NCI_STATUS_EXT_FAILED;
            break;
    }
    nfa_rw_release_lock();
    return ret;
}

tNCI_RW_WDST nfa_rw_watch_dog_get_state() {
    nfa_rw_request_lock();
    tNCI_RW_WDST state = s_state;
    nfa_rw_release_lock();
    return state;
}

#endif // IS_FEATURE_ON(NCI_FEATURE_RW_EXT)
